
namespace Patterns4Net.Core
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using System.Linq.Expressions;
    using System.Text.RegularExpressions;
    using Mono.Cecil;
    using Patterns4Net.Attributes.DesignPatterns;
    using Patterns4Net.Core.Patterns;
    using Patterns4Net.Attributes;

    /// <summary>
    /// Provides convention that matches members with specific name.
    /// The rules for a name are entered using a Regex, for the most usual cases
    /// there are factory methods e.g. <see cref="StartsWith(string,IPattern)"/>.
    /// The pattern that is returned when the class matches this convention can be configured 
    /// using simply just an instance or an expression which takes 
    /// <see cref="IMemberDefinition"/> of the member as a parameter and returns the pattern instance.
    /// </summary>
    [Immutable]
    public sealed class NameConvention : IPatternsProvider
    {
        private readonly Regex regex;
        private readonly Expression<Func<IMemberDefinition, IPattern>> patternExpression;

        public NameConvention(Regex regex, Expression<Func<IMemberDefinition, IPattern>> patternExpression)
        {
            Contract.Requires(patternExpression != null);
            Contract.Requires(regex != null);
            this.regex = regex;
            this.patternExpression = patternExpression;
        }

        #region SimpleFactoryMethods

        [SimpleFactoryMethod]
        public static NameConvention StartsWith(string str, Expression<Func<IMemberDefinition, IPattern>> patternExpression)
        {
            Contract.Requires(patternExpression != null);
            Contract.Ensures(Contract.Result<NameConvention>() != null);
            return new NameConvention(new Regex(string.Format("^{0}.*$", str)), patternExpression);
        }

        [SimpleFactoryMethod]
        public static NameConvention EndsWith(string str, Expression<Func<IMemberDefinition, IPattern>> patternExpression)
        {
            Contract.Requires(patternExpression != null);
            Contract.Ensures(Contract.Result<NameConvention>() != null);
            return new NameConvention(new Regex(string.Format("^.*{0}$", str)), patternExpression);
        }

        [SimpleFactoryMethod]
        public static NameConvention Contains(string str, Expression<Func<IMemberDefinition, IPattern>> patternExpression)
        {
            Contract.Requires(patternExpression != null);
            Contract.Ensures(Contract.Result<NameConvention>() != null);
            return new NameConvention(new Regex(string.Format("^.*{0}.*$", str)), patternExpression);
        }

        #endregion

        public IEnumerable<IPattern> GetPatterns<TMember>(TMember member) where TMember : IMemberDefinition
        {
            /* Contracts are inherited. */

            Contract.Assume(member.Name != null);
            if (this.regex.IsMatch(member.Name)) 
            {
                var compiled = this.patternExpression.Compile();
                Contract.Assume(compiled != null);
                return new[] { compiled.Invoke(member) };
            }

            return new IPattern[0];
        }

        [ContractInvariantMethod]
        private void Invariants()
        {
            Contract.Invariant(this.regex != null);
            Contract.Invariant(this.patternExpression != null);
        }
    }
}
